package App.Utility;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class HttpClient {
    private HttpUrl.Builder urlBuilder;

    private static final String TYPE = "SSL";

    private Request.Builder reqtBuilder = new Request.Builder();

    private List<Request> requests = new ArrayList<Request>();

    private String url;

    public static final int READ_TIME_OUT_DEFAULT = 120;

    public static final int CONNECT_TIME_OUT = 15;

    private OkHttpClient client;

    private HttpClient(String url, String type, int connect_time_out,
                       int read_time_out) {
        this.url = url;
        if (type != null && "SSL".equals(type)) {
            this.client = new OkHttpClient()
                    .newBuilder()
                    .sslSocketFactory(createSSLSocketFactory())
                    .hostnameVerifier(new HostnameVerifier() {
                        public boolean verify(String hostname,
                                              SSLSession session) {
                            return true;
                        }
                    })
                    .connectTimeout(
                            connect_time_out > 0 ? connect_time_out
                                    : CONNECT_TIME_OUT, TimeUnit.SECONDS)
                    .readTimeout(
                            read_time_out > 0 ? read_time_out
                                    : READ_TIME_OUT_DEFAULT, TimeUnit.SECONDS)
                    .build();
        } else {
            this.client = new OkHttpClient()
                    .newBuilder()
                    .connectTimeout(
                            connect_time_out > 0 ? connect_time_out
                                    : CONNECT_TIME_OUT, TimeUnit.SECONDS)
                    .readTimeout(
                            read_time_out > 0 ? read_time_out
                                    : READ_TIME_OUT_DEFAULT, TimeUnit.SECONDS)
                    .build();
        }

    }

    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[] { new TrustAllCerts() },
                    new SecureRandom());

            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
        }
        return ssfFactory;
    }

    public static HttpClient getHttpsRequest(String url, int connect_time_out,
                                             int read_time_out) {
        return new HttpClient(url, TYPE, connect_time_out, read_time_out);
    }

    public static HttpClient getHttpsRequest(String url) {
        return getHttpsRequest(url, 0, 0);
    }

    public static HttpClient getHttpRequest(String url, int connect_time_out,
                                            int read_time_out) {
        return new HttpClient(url, null, connect_time_out, read_time_out);
    }

    public static HttpClient getHttpRequest(String url) {
        return getHttpRequest(url, 0, 0);
    }

    public HttpClient addHeader(String name, String value) {
        if (name == null || value == null) {
            return this;
        }
        reqtBuilder.addHeader(name, value);
        return this;
    }

    public HttpClient addHeader(String name, Long value) {
        if (value == null) {
            return this;
        }
        return addHeader(name, String.valueOf(value));
    }

    public HttpClient addHeader(Map<String, String> headerMap) {
        if (headerMap != null && headerMap.size() > 0) {
            for (String key : headerMap.keySet()) {
                addHeader(key, headerMap.get(key));
            }
        }
        return this;
    }

    public HttpClient addQueryParam(String name, String value) {
        if (urlBuilder == null) {
            urlBuilder = toUrlBuilder(url);
        }
        urlBuilder.addQueryParameter(name, value);
        return this;
    }

    private Request.Builder request() {
        if (urlBuilder == null) {
            reqtBuilder.url(url);
        } else {
            reqtBuilder.url(urlBuilder.build());
        }
        return reqtBuilder;
    }

    public Response doPost(RequestBody body) throws IOException {
        Request.Builder reqt = request();
        Request request = reqt.post(body).build();
        this.requests.add(request);
        return execute(request);
    }

    public Response doGet() throws IOException {
        Request.Builder reqt = request();
        Request request = reqt.get().build();
        this.requests.add(request);
        return execute(request);
    }

    public Response doDelete() throws IOException {
        Request.Builder reqt = request();
        Request request = reqt.delete().build();
        this.requests.add(request);
        return execute(request);
    }

    public Response doPut(RequestBody body) throws IOException {
        Request.Builder reqt = request();
        Request request = reqt.put(body).build();
        this.requests.add(request);
        return execute(request);
    }

    private Response execute(Request request) throws IOException {
        return client.newCall(request).execute();
    }

    private HttpUrl.Builder toUrlBuilder(String url) {
        HttpUrl base = HttpUrl.parse(url);
        HttpUrl.Builder urlBuilder = new HttpUrl.Builder();
        urlBuilder.scheme(base.scheme());
        urlBuilder.host(base.host());
        urlBuilder.port(base.port());
        if (base.pathSize() > 0) {
            for (String pathSegment : base.pathSegments()) {
                urlBuilder.addPathSegment(pathSegment);
            }
        }
        urlBuilder.query(base.query());
        return urlBuilder;
    }
}
